/******************************************************************
 *  MRRM's pipeline register control unit (CPU)                   *
 *                                                                *
 *  This file is part of the MRRM project                         *
 *  <http://mrrm.googlecode.com/>                                 *
 *                                                                *
 *  Author(s):                                                    *
 *    -  Yuan Pengfei <coolypf@opencores.org>                     *
 *                                                                *
 ******************************************************************
 *                                                                *
 *  Copyright (C) 2010 AUTHORS                                    *
 *                                                                *
 *  This source file may be used and distributed without          *
 *  restriction provided that this copyright statement is not     *
 *  removed from the file and that any derivative work contains   *
 *  the original copyright notice and the associated disclaimer.  *
 *                                                                *
 *  MRRM is free software: you can redistribute it and/or modify  *
 *  it under the terms of the GNU General Public License as       *
 *  published by the Free Software Foundation, either version 3   *
 *  of the License, or (at your option) any later version.        *
 *                                                                *
 *  MRRM is distributed in the hope that it will be useful, but   *
 *  WITHOUT ANY WARRANTY; without even the implied warranty of    *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  *
 *  GNU General Public License for more details.                  *
 *                                                                *
 *  You should have received a copy of the GNU General Public     *
 *  License along with MRRM. If not, see                          *
 *  <http://www.gnu.org/licenses/>.                               *
 *                                                                *
 ******************************************************************/

`include "global.v"

module mrrm_cpu(
	// Clock and reset
	clk, rst,
	// Memory access interface
	imemaddr, imemdata,
	dmemaddr, dmemwr, dmemrd, dmemdataw, dmemdatar, dmembyte
	);

parameter rw = `REGFILE_ADDR_WIDTH;
parameter mw = `MEMORY_ADDR_WIDTH;
parameter iw = `INSTRUCTION_WIDTH;
parameter ow = `OPERAND_WIDTH;

// Interfaces
// Clock and reset
input clk;
input rst;

// Memory access
output [mw-1:0] imemaddr;
input [iw-1:0] imemdata;
output [mw-1:0] dmemaddr;
output dmemwr;
output dmemrd;
output [ow-1:0] dmemdataw;
input [ow-1:0] dmemdatar;
output dmembyte;

// Internal wires
// Pipeline
wire F_bubble;
wire F_stall;
wire D_bubble;
wire D_stall;
wire E_bubble;
wire E_stall;
wire M_bubble;
wire M_stall;
wire W_bubble;
wire W_stall;

// RF
wire [rw-1:0] rf_addra;
wire [rw-1:0] rf_addrb;
wire [rw-1:0] rf_addrc;
wire [rw-1:0] rf_addrx;
wire [rw-1:0] rf_addry;
wire [ow-1:0] rf_dataa;
wire [ow-1:0] rf_datab;
wire [ow-1:0] rf_datac;
wire [ow-1:0] rf_datax;
wire [ow-1:0] rf_datay;
wire rf_wrx;
wire rf_wry;

// IF
wire [iw-1:0] f_insn;
wire [mw-1:0] f_valp;
wire [mw-1:0] f_predpc;
wire halt;

// ID
wire [ow-1:0] d_vala;
wire [ow-1:0] d_valb;
wire [ow-1:0] d_valc;
wire [ow-1:0] d_vale;
wire [2:0] d_itype;
wire [1:0] d_isubtype;
wire [3:0] d_ifun;
wire d_snl;
wire [rw-1:0] d_dstx;
wire [rw-1:0] d_dsty;
wire d_wrx;
wire d_wry;
wire [rw-1:0] d_srca;
wire [rw-1:0] d_srcb;
wire [rw-1:0] d_srcc;

// EXE
wire [ow-1:0] e_valx;
wire [ow-1:0] e_valy;
wire e_bch;

// MEM
wire [ow-1:0] m_valm;

// Pipeline regs
// Status
reg F_halt;
reg D_halt;
reg E_halt;
reg M_halt;
reg W_halt;

// IF
reg [mw-1:0] F_predpc;

// ID
reg [iw-1:0] D_insn;
reg [mw-1:0] D_valp;

// EXE
reg [mw-1:0] E_valp;
reg [ow-1:0] E_vala;
reg [ow-1:0] E_valb;
reg [ow-1:0] E_valc;
reg [ow-1:0] E_vale;
reg [2:0] E_itype;
reg [1:0] E_isubtype;
reg [3:0] E_ifun;
reg E_snl;
reg [rw-1:0] E_dstx;
reg [rw-1:0] E_dsty;
reg E_wrx;
reg E_wry;

// MEM
reg [mw-1:0] M_valp;
reg [2:0] M_itype;
reg [3:0] M_ifun;
reg M_snl;
reg M_bch;
reg [ow-1:0] M_valx;
reg [ow-1:0] M_valy;
reg [ow-1:0] M_vale;
reg [rw-1:0] M_dstx;
reg [rw-1:0] M_dsty;
reg M_wrx;
reg M_wry;

// WB
reg [2:0] W_itype;
reg W_snl;
reg [ow-1:0] W_valx;
reg [ow-1:0] W_valy;
reg [ow-1:0] W_valm;
reg [rw-1:0] W_dstx;
reg [rw-1:0] W_dsty;
reg W_wrx;
reg W_wry;

// Instances
mrrm_rf cpu_rf(
	.clk(clk), .rst(rst),
	.addrx(rf_addrx), .addry(rf_addry),
	.datax(rf_datax), .datay(rf_datay),
	.wrx(rf_wrx), .wry(rf_wry),
	.addra(rf_addra), .addrb(rf_addrb), .addrc(rf_addrc),
	.dataa(rf_dataa), .datab(rf_datab), .datac(rf_datac)
	);

mrrm_if cpu_if(
	.imemaddr(imemaddr), .imemdata(imemdata),
	.F_predpc(F_predpc), .f_insn(f_insn), .f_valp(f_valp), .f_predpc(f_predpc),
	.halt(halt),
	.E_itype(E_itype), .E_isubtype(E_isubtype), .E_ifun(E_ifun), .E_valb(E_valb),
	.M_bch(M_bch), .M_itype(M_itype), .M_valp(M_valp)
	);

mrrm_id cpu_id(
	.D_insn(D_insn), .D_valp(D_valp),
	.d_vala(d_vala), .d_valb(d_valb), .d_valc(d_valc), .d_vale(d_vale),
	.d_itype(d_itype), .d_isubtype(d_isubtype), .d_ifun(d_ifun), .d_snl(d_snl),
	.d_dstx(d_dstx), .d_dsty(d_dsty), .d_wrx(d_wrx), .d_wry(d_wry),
	.addra(rf_addra), .addrb(rf_addrb), .addrc(rf_addrc),
	.dataa(rf_dataa), .datab(rf_datab), .datac(rf_datac),
	.E_dstx(E_dstx), .E_dsty(E_dsty), .E_wrx(E_wrx), .E_wry(E_wry),
	.e_valx(e_valx), .e_valy(e_valy),
	.M_itype(M_itype[2:1]), .M_snl(M_snl),
	.M_dstx(M_dstx), .M_dsty(M_dsty), .M_wrx(M_wrx), .M_wry(M_wry),
	.M_valx(M_valx), .M_valy(M_valy), .m_valm(m_valm),
	.W_itype(W_itype[2:1]), .W_snl(W_snl),
	.W_dstx(W_dstx), .W_dsty(W_dsty), .W_wrx(W_wrx), .W_wry(W_wry),
	.W_valx(W_valx), .W_valy(W_valy), .W_valm(W_valm),
	.d_srca(d_srca), .d_srcb(d_srcb), .d_srcc(d_srcc)
	);

mrrm_exe cpu_exe(
	.clk(clk), .rst(rst),
	.E_itype(E_itype), .E_isubtype(E_isubtype), .E_ifun(E_ifun), .E_snl(E_snl),
	.E_vala(E_vala), .E_valb(E_valb), .E_valc(E_valc),
	.e_valx(e_valx), .e_valy(e_valy), .e_bch(e_bch)
	);

mrrm_mem cpu_mem(
	.dmemaddr(dmemaddr), .dmemwr(dmemwr), .dmemrd(dmemrd),
	.dmemdataw(dmemdataw), .dmemdatar(dmemdatar), .dmembyte(dmembyte),
	.M_itype(M_itype[2:1]), .M_ifun_b(M_ifun[1]), .M_snl(M_snl),
	.M_valx(M_valx), .M_vale(M_vale), .m_valm(m_valm)
	);

mrrm_wb cpu_wb(
	.addrx(rf_addrx), .addry(rf_addry),
	.datax(rf_datax), .datay(rf_datay),
	.wrx(rf_wrx), .wry(rf_wry),
	.W_itype(W_itype[2:1]),
	.W_valx(W_valx), .W_valy(W_valy), .W_valm(W_valm),
	.W_dstx(W_dstx), .W_dsty(W_dsty), .W_wrx(W_wrx), .W_wry(W_wry)
	);

// Pipeline register control
assign F_bubble = 1'b0;
assign F_stall = (
	({E_itype[2:1], E_snl} == 3'b011 && E_wrx == 1'b1 &&
		(E_dstx == d_srca || E_dstx == d_srcb || E_dstx == d_srcc))
	|| ({D_insn[31:29], D_insn[8], D_insn[5], D_insn[28:25]} == {`I_REG, `S_TWO, `F_JUMP})
	) ? 1'b1 : 1'b0;

assign D_bubble = (
	({E_itype, e_bch} == {`I_BXX, 1'b0})
	|| (!({E_itype[2:1], E_snl} == 3'b011 && E_wrx == 1'b1 &&
		(E_dstx == d_srca || E_dstx == d_srcb || E_dstx == d_srcc))
	&& ({D_insn[31:29], D_insn[8], D_insn[5], D_insn[28:25]} == {`I_REG, `S_TWO, `F_JUMP}))
	) ? 1'b1 : 1'b0;
assign D_stall = (
	{E_itype[2:1], E_snl} == 3'b011 && E_wrx == 1'b1 &&
		(E_dstx == d_srca || E_dstx == d_srcb || E_dstx == d_srcc)
	) ? 1'b1 : 1'b0;

assign E_bubble = (
	({E_itype, e_bch} == {`I_BXX, 1'b0})
	|| ({E_itype[2:1], E_snl} == 3'b011 && E_wrx == 1'b1 &&
		(E_dstx == d_srca || E_dstx == d_srcb || E_dstx == d_srcc))
	) ? 1'b1 : 1'b0;
assign E_stall = 1'b0;

assign M_bubble = 1'b0;
assign M_stall = 1'b0;

assign W_bubble = 1'b0;
assign W_stall = 1'b0;

// CPU status
always @(posedge clk or `RST_EVENT rst)
	if(rst == `RST_VALUE) begin
		F_halt <= 1'b0;
		D_halt <= 1'b0;
		E_halt <= 1'b0;
		M_halt <= 1'b0;
		W_halt <= 1'b0;
	end
	else begin
		if(halt == 1'b1) begin
			F_halt <= 1'b1;
			D_halt <= 1'b1;
		end
		E_halt <= D_halt;
		M_halt <= E_halt;
		W_halt <= M_halt;
	end

// Pipeline register controll
always @(posedge clk or `RST_EVENT rst)
	if(rst == `RST_VALUE) begin
		F_predpc <= 0;
		D_insn <= 0;
		D_valp <= 0;
		E_valp <= 0;
		E_vala <= 0;
		E_valb <= 0;
		E_valc <= 0;
		E_vale <= 0;
		E_itype <= 0;
		E_isubtype <= 0;
		E_ifun <= 0;
		E_snl <= 0;
		E_dstx <= 0;
		E_dsty <= 0;
		E_wrx <= 0;
		E_wry <= 0;
		M_valp <= 0;
		M_itype <= 0;
		M_ifun <= 0;
		M_snl <= 0;
		M_bch <= 0;
		M_valx <= 0;
		M_valy <= 0;
		M_vale <= 0;
		M_dstx <= 0;
		M_dsty <= 0;
		M_wrx <= 0;
		M_wry <= 0;
		W_itype <= 0;
		W_snl <= 0;
		W_valx <= 0;
		W_valy <= 0;
		W_valm <= 0;
		W_dstx <= 0;
		W_dsty <= 0;
		W_wrx <= 0;
		W_wry <= 0;
	end
	else begin
		// IF
		if(F_stall == 1'b1 || F_halt == 1'b1) begin
			// Nothing
		end
		else if(F_bubble == 1'b1) begin
			// Nothing
		end
		else begin
			F_predpc <= f_predpc;
		end
		
		// ID
		if(D_stall == 1'b1 || D_halt == 1'b1) begin
			// Nothing
		end
		else if(D_bubble == 1'b1) begin
			D_insn <= {`I_NOP, 29'b0};
		end
		else begin
			D_insn <= f_insn;
			D_valp <= f_valp;
		end
		
		// EXE
		if(E_stall == 1'b1 || E_halt == 1'b1) begin
			// Nothing
		end
		else if(E_bubble == 1'b1) begin
			E_itype <= `I_NOP;
			E_wrx <= 0;
			E_wry <= 0;
		end
		else begin
			E_valp <= D_valp;
			E_vala <= d_vala;
			E_valb <= d_valb;
			E_valc <= d_valc;
			E_vale <= d_vale;
			E_itype <= d_itype;
			E_isubtype <= d_isubtype;
			E_ifun <= d_ifun;
			E_snl <= d_snl;
			E_dstx <= d_dstx;
			E_dsty <= d_dsty;
			E_wrx <= d_wrx;
			E_wry <= d_wry;
		end
		
		// MEM
		if(M_stall == 1'b1 || M_halt == 1'b1) begin
			// Nothing
		end
		else if(M_bubble == 1'b1) begin
			M_itype <= `I_NOP;
			M_bch <= 0;
			M_wrx <= 0;
			M_wry <= 0;
		end
		else begin
			M_valp <= E_valp;
			M_itype <= E_itype;
			M_ifun <= E_ifun;
			M_snl <= E_snl;
			M_bch <= e_bch;
			M_valx <= e_valx;
			M_valy <= e_valy;
			M_vale <= E_vale;
			M_dstx <= E_dstx;
			M_dsty <= E_dsty;
			M_wrx <= E_wrx;
			M_wry <= E_wry;
		end
		
		// WB
		if(W_stall == 1'b1 || W_halt == 1'b1) begin
			// Nothing
		end
		else if(W_bubble == 1'b1) begin
			W_itype <= `I_NOP;
			W_wrx <= 0; 
			W_wry <= 0;
		end
		else begin
			W_itype <= M_itype;
			W_snl <= M_snl;
			W_valx <= M_valx;
			W_valy <= M_valy;
			W_valm <= m_valm;
			W_dstx <= M_dstx;
			W_dsty <= M_dsty;
			W_wrx <= M_wrx;
			W_wry <= M_wry;
		end
	end

endmodule
